/*
Copyright (C) 2023 Intel Corporation

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions
and limitations under the License.
*/

#ifndef BACKENDS_TC_INTROSPECTION_H_
#define BACKENDS_TC_INTROSPECTION_H_

#include "frontends/p4/parseAnnotations.h"
#include "frontends/p4/parserCallGraph.h"
#include "ir/ir.h"
#include "lib/json.h"
#include "lib/nullstream.h"
#include "options.h"
#include "tcAnnotations.h"

/// This file declares the different structures to be used in the introspection json file and the
/// pass to generate the file An introspection json file generated by the tc backend is used for
/// control plane programming by P4TC in Linux kernel
namespace P4::TC {

using namespace P4::literals;

struct IntrospectionInfo {
    cstring schemaVersion;
    cstring pipelineName;
    IntrospectionInfo() {
        schemaVersion = nullptr;
        pipelineName = nullptr;
    }
    void initIntrospectionInfo(IR::TCPipeline *tcPipeline) {
        schemaVersion = "1.0.0"_cs;
        pipelineName = tcPipeline->pipelineName;
    }
};

struct KeyFieldAttributes {
    unsigned int id;
    cstring name;
    cstring type;
    cstring matchType;
    cstring attribute;
    unsigned int bitwidth;
    KeyFieldAttributes() {
        id = 0;
        name = nullptr;
        type = nullptr;
        matchType = nullptr;
        attribute = nullptr;
        bitwidth = 0;
    }
};

struct Annotation {
    cstring name;
    Annotation() { name = nullptr; }
    explicit Annotation(cstring n) { name = n; }
};

struct ActionParam {
    unsigned int id;
    cstring name;
    unsigned int dataType;
    unsigned int bitwidth;
    ActionParam() {
        id = 0;
        name = nullptr;
        bitwidth = 0;
    }
};

enum ActionScope { TableOnly, DefaultOnly, TableAndDefault };

struct ActionAttributes {
    unsigned int id;
    cstring name;
    ActionScope scope;
    bool defaultHit;
    bool defaultMiss;
    safe_vector<struct Annotation *> annotations;
    safe_vector<struct ActionParam *> actionParams;
    ActionAttributes() {
        id = 0;
        name = nullptr;
        scope = TableAndDefault;
        defaultHit = false;
        defaultMiss = false;
    }
};

struct TableAttributes {
    cstring name;
    cstring permissions;
    unsigned int id;
    unsigned int tentries;
    unsigned int numMask;
    unsigned int keysize;
    unsigned int keyid;
    safe_vector<struct KeyFieldAttributes *> keyFields;
    safe_vector<struct ActionAttributes *> actions;
    TableAttributes() {
        name = nullptr;
        permissions = nullptr;
        id = 0;
        tentries = 0;
        numMask = 0;
        keysize = 0;
        keyid = 0;
    }
};

struct ExternInstancesAttributes {
    unsigned int id;
    cstring name;
    cstring type;
    safe_vector<struct KeyFieldAttributes *> keyFields;
    ExternInstancesAttributes() {
        id = 0;
        name = nullptr;
        type = nullptr;
    }
};

struct ExternAttributes {
    cstring name;
    cstring permissions;
    cstring id;
    safe_vector<struct ExternInstancesAttributes *> instances;
    ExternAttributes() {
        name = nullptr;
        permissions = nullptr;
        id = 0;
    }
};

/// This pass generates introspection JSON into user specified file
class IntrospectionGenerator : public Inspector {
    IR::TCPipeline *tcPipeline;
    P4::ReferenceMap *refMap;
    P4::TypeMap *typeMap;
    safe_vector<struct ExternAttributes *> externsInfo;
    safe_vector<struct TableAttributes *> tablesInfo;
    ordered_map<cstring, const IR::P4Table *> p4tables;

 public:
    IntrospectionGenerator(IR::TCPipeline *tcPipeline, P4::ReferenceMap *refMap,
                           P4::TypeMap *typeMap)
        : tcPipeline(tcPipeline), refMap(refMap), typeMap(typeMap) {}
    void postorder(const IR::P4Table *t);
    const Util::JsonObject *genIntrospectionJson();
    void genExternJson(Util::JsonArray *externJson);
    Util::JsonObject *genExternInfo(struct ExternAttributes *extn);
    void genTableJson(Util::JsonArray *tablesJson);
    Util::JsonObject *genTableInfo(struct TableAttributes *tbl);
    void collectTableInfo();
    void collectExternInfo();
    void collectKeyInfo(const IR::Key *k, struct TableAttributes *tableinfo);
    void collectActionInfo(const IR::ActionList *actionlist, struct TableAttributes *tableinfo,
                           const IR::P4Table *p4table, const IR::TCTable *table);
    Util::JsonObject *genActionInfo(struct ActionAttributes *action);
    Util::JsonObject *genKeyInfo(struct KeyFieldAttributes *keyField);
    bool serializeIntrospectionJson(std::ostream &destination);
    std::optional<cstring> checkValidTcType(const IR::StringLiteral *sl);
    cstring externalName(const IR::IDeclaration *declaration);
};

}  // namespace P4::TC

#endif /* BACKENDS_TC_INTROSPECTION_H_ */
